package com.sl.core.engine.datasource;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.db.Entity;
import cn.hutool.db.handler.EntityListHandler;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.hop.core.database.Database;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.exception.HopDatabaseException;
import org.apache.hop.core.logging.ILoggingObject;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.row.value.ValueMetaString;
import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class SlDatabase extends Database {

    /**
     * Construct a new Database Connection
     *
     * @param parentObject The parent
     * @param variables
     * @param databaseMeta The Database Connection Info to construct the connection with.
     */
    public SlDatabase(ILoggingObject parentObject, IVariables variables, DatabaseMeta databaseMeta) {
        super(parentObject, variables, databaseMeta);
    }

    @SneakyThrows
    public List<IValueMeta> getIndexList(String tableName) {
        List<String> names = new ArrayList<>();
        ResultSet allkeys = null;
        List<IValueMeta> result = Lists.newArrayList();

        try {
            allkeys = getDatabaseMetaData().getIndexInfo(null, null, tableName, false, true);
            List<Entity> handle = EntityListHandler.create().handle(allkeys);
            List<IValueMeta> collect = handle.stream().map(v -> {
                IValueMeta valueMeta = new ValueMetaString();
                String name = v.getStr("COLUMN_NAME");
                String indexName = v.getStr("INDEX_NAME");
                Boolean nonUnique = v.getBean("NON_UNIQUE");
                v.getStr("TABLE_CAT");
                String tableSchem = v.getStr("TABLE_SCHEM");
                String indexQualifier = v.getStr("INDEX_QUALIFIER");
                Integer ordinalPosition = v.getInt("ORDINAL_POSITION");
                String ascOrDesc = v.getStr("ASC_OR_DESC");
                Long cardinality = v.getLong("CARDINALITY");
                Long pages = v.getLong("PAGES");

                Integer type = v.getInt("TYPE");
//                int size = columns.getInt("COLUMN_SIZE");
                valueMeta.setName(indexName);
                valueMeta.setComments(name);
                valueMeta.setOrigin(ascOrDesc);
                valueMeta.setCaseInsensitive(nonUnique);
                return valueMeta;
            }).collect(Collectors.toList());

            result.addAll(collect);

        } catch (SQLException e) {
            // log.logError(toString(), "Error getting primary keys columns from table [" + tableName + "]");
        } finally {
            try {
                if (allkeys != null) {
                    allkeys.close();
                }
            } catch (SQLException e) {
//                log.logError(
//                        "Error closing connection while searching primary keys in table [" + tableName + "]",
//                        e);
            }
        }
        return result;
    }

    public String removeTable(String tableName) {
        return "drop table " + tableName;
    }

    public String getDDLModifyTable(String sourceTableName, String tableName) {
        return "ALTER TABLE " + sourceTableName + " rename to " + tableName;
    }

    public String getRemoveIndexStatement(String catalogName, String tableName, String indexName) {
        return "drop index " + indexName;
    }

    @Override
    public Map<String, Collection<String>> getTableMap(String schemanamein, Map<String, String> props) throws HopDatabaseException {
        String schemaname = schemanamein;
        DatabaseMeta databaseMeta = getDatabaseMeta();
        DatabaseMetaData databaseMetaData = getDatabaseMetaData();
        if (schemaname == null && databaseMeta.useSchemaNameForTableList()) {
            schemaname = resolve(databaseMeta.getUsername()).toUpperCase();
        }
        Map<String, Collection<String>> tableMap = new HashMap<>();
        ResultSet alltables = null;
        try {
            alltables =
                    databaseMetaData.getTables(schemaname, schemaname, null, databaseMeta.getTableTypes());
            while (alltables.next()) {
                String cat = "";
                try {
                    cat = alltables.getString("TABLE_CAT");
                } catch (Exception e) {
                    // ignore
                    if (log.isDebugEnabled()) {
                        log.debug("Error getting tables for field TABLE_CAT (ignored): " + e.toString());
                    }
                }

                String schema = "";
                try {
                    schema = alltables.getString("TABLE_SCHEM");
                } catch (Exception e) {
                    // ignore
                    if (log.isDebugEnabled()) {
                        log.debug("Error getting tables for field TABLE_SCHEM (ignored): " + e.toString());
                    }
                }

                if (Utils.isEmpty(schema)) {
                    schema = cat;
                }

                String table = alltables.getString("TABLE_NAME");


                // Check for any extra properties that might require validation
                if (props != null && !props.isEmpty()) {
                    for (Map.Entry<String, String> prop : props.entrySet()) {
                        String propName = prop.getKey();

                        String tableProperty = alltables.getString(propName);
                        if (tableProperty != null) {
                            String propValue = prop.getValue();

                            if (tableProperty.equals(propValue)) {
//                                multimapPut(schema, table, tableMap);
                                ReflectUtil.invoke(this, "multimapPut", schema, table, tableMap);

                            }
                        }
                    }
                } else {
//                    multimapPut(schema, table, tableMap);
                    ReflectUtil.invoke(this, "multimapPut", schema, table, tableMap);

                }
            }
        } catch (SQLException e) {
            log.error("Error getting tablenames from schema [" + schemaname + "]");
        } finally {
            try {
                if (alltables != null) {
                    alltables.close();
                }
            } catch (SQLException e) {
                throw new HopDatabaseException(
                        "Error closing resultset after getting views from schema [" + schemaname + "]", e);
            }
        }

        if (log.isDebugEnabled()) {
            int i = ReflectUtil.invoke(this, "multimapSize", tableMap);
            log.debug("read :" + i + " table names from db meta-data.");
        }

        return tableMap;
    }
}
